home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-17
/
herkules.zip
/
HERKULES.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-08-22
|
64KB
|
1,274 lines
; Assemble as COM-File!!!
;Dieses Programm hängt sich als Treiber in den INT10 ein und übernimmt Aufrufe
;in den VideoModes 7 und 8. Letzterer ist der von diesem Programm neu unter-
;stützte Herkules GraphikMode mit 720x360 Punkten. Der Vorteil von diesem Pro-
;gramm liegt darin, daß z.B. MSDOS nun auch im Herkules-GraphMode die Zeichen
;richtig darstellt. Leider müssen im GraphikMode beim Scrollen 32kB verschoben
;werden, was die Sache deutlich bremst. Um das zu umgehen, versucht das Pro-
;gramm, solche aufwendigen Scrolls durch Verschieben der DisplayStart-Adresse
;zu Beschleunigen. Ein Beispiel: Wenn der ganze Screen um eine Zeile nach oben
;geschoben werden soll, so muß man dazu 32670 Bytes bewegen. Setzt man aber
;die DisplayStart-Adresse des 6845 um 90 Bytes weiter, so hat das den
;gleichen Effekt wie das Scrollen, ist aber bedeutent schneller. Aber:
;Wenn man nur einen Teil des Screens verschieben will, so wird beim Umpro-
;grammieren des 6845 der Rest mitverschoben. Letzter muß daher vorher in die
;umgekehrte Richtung gescrolled werden, nach dem Ändern des DispStart ist
;er dann wieder an der richtigen Stelle. Beispiel:
; ╔═════╤═════════════════════════════╤═════╗
; ║ │ 3 │ ║
; ║ ╔═════════════════════════════╗ ║
; ║ ║ ║ ║
; ║ ║ ║ ║
; ║ ║ ║ ║
; ║ 2 ║ 1 ║ 4 ║
; ║ ║ ║ ║
; ║ ║ ║ ║
; ║ ║ ║ ║
; ║ ╚═════════════════════════════╝ ║
; ║ │ 3 │ ║
; ╚═════╧═════════════════════════════╧═════╝
;Soll Window 1 um eine Zeile nach oben gescrolled werden, so werden statt
;dessen nacheinander Bereiche 2, 3 und 4 (gehen über die vertikalen
;Bildschirmgrenzen herum!) um eine Zeile nach unten rotiert(!),
;und dann der DispStart um eine Zeile nach unten versetzt.
;Der Bildschirmspeicher der Herkules-Karte im Graphik-Modus ist in vier
;BitMaps unterteilt, die an den Adressen $B0000, $B2000, $B4000, $B6000
;beginnen, bzw in der zweiten Page $B8000, $BA000, $BC000 und $BE000.
;Aufeinanderfolgende Zeilen entstammen folgenden BitMaps:
;0 : BitMap 0 Zeile 0
;1 : BitMap 1 Zeile 0
;2 : BitMap 2 Zeile 0
;3 : BitMap 3 Zeile 0
;4 : BitMap 0 Zeile 1
;5 : BitMap 1 Zeile 1
;und so weiter. Vier aufeinanderfolgende Zeilen aus den BitMaps 0, 1, 2 und 3
;nennt man einen "Scan". Ein HiresZeichen aus dem 8x8-Font besteht somit aus
;2 Scans. Das Programm 'versteht' im Graphik-Modus nur zwei Attribut-Nibbles,
;von denen das High-Nibble für den Hintergrund und das Low-Nibble für das
;Zeichen zuständig sind. Sind die 3 LSBs eines Nibbles 0, so wird schwarz,
;in allen anderen Fällen weiß geschrieben.
;Zur Drucker-Ansteuerung bei Hardcopies müssen eventuell 6 Pascal-Strings
;angepasst werden, die sich im erzeugten COM-File nach dem Laden durch
;Debug an Offsets $110, $120, $130 etc befinden. Jeder String besteht aus
;einem LängenByte und maximal 15 Zeichen. Der erste wird am Beginn, der zweite
;am Ende einer TextScreen-HardCopy zum Drucker mit der Nummer, die an Offset
;$108 steht. Die nächsten zwei werden am Anfang sowie am Ende einer Graph-
;Screen-HardCopy gesendet, die letzten zwei am Anfang bzw am Ende jeder Zeile
;der Graphik-Hardcopy. Sie sollten den Drucker darüber informieren, daß jetzt
;720 Bytes 8Bit-Graphik kommen (wobei die 8 Bit SENKRECHT stehen!) bzw das
;Papier eine Zeile (bei Epson-Druckern also 8/72, nicht 1/6 Zoll) hoch-
;schieben.
;=============================================================================
BsLineUp EQU 0 ;Wenn der Cursor am Anfang der Zeile steht und dann ein
;CHR(8) kommt, soll der Cursor dann einfach dort stehen
;bleiben (0) oder auf das letzte Zeichen der vorherigen
;Zeile gesetzt werden (<>0) ?
OptChar EQU '/'
False EQU 0
True EQU NOT False
Dunnow EQU 01010101b ;(weder False noch True...)
Ret_Ok EQU 0
Ret_Expl EQU 1
Ret_Error EQU 2
NrOfDotsX EQU 720
NrOfDotsY EQU 360
ScrSize EQU NrOfDotsX/8*NrOfDotsY
NrOfChars EQU NrOfDotsX/8 ;Im GraphikMode wird ein
NrOfLines EQU NrOfDotsY/8 ;8x8-Zeichensatz benutzt.
WholeScr EQU NrOfChars*NrOfLines
BufSize EQU WholeScr*2/2 ;Zum Rotieren wird im GraphikMode ein Buffer
;benötigt. Da jede der 4 Pages einzeln rotiert wird, braucht man
;pro Zeichen keine 8 sondern nur 2 Bytes. Außerdem wird um maximal
;die halbe Bildschirm-Höhe rotiert, sonst würde andersrum rotiert.
StackSize EQU 2*(24+24) ;24 Worte für mich und 24 für sonstige Irq Routine.
Page0Seg EQU 0B000h
Page1Seg EQU 0B800h
HGC_Diag EQU 00b
HGC_Text EQU 10b
HGC_Full EQU 11b
IndexReg EQU 3B4h
DataReg EQU IndexReg+1
ModeReg EQU 3B8h
StatusReg EQU 3BAh
ConfigReg EQU 3BFh
;=============================================================================
;Hinter "IndexReg" und "DataReg" verbergen sich 16 Register des 6845. Um auf
;eines zugreifen zu können, muß seine Nummer in das "IndexReg" geschrieben
;werden, dann kann das selektierte Register durch das "DataReg" gelesen oder
;beschrieben werden.
;Regs 0-3 : Timing innerhalb einer Zeile
;Regs 4-7 : Timing innerhalb des Bildes
;Regs 8-9 : Miscellanous (wie schreibt man das?)
;Regs 10-11 : CursorShape
;Regs 12-13 : DispStart-Adress (Hi und Lo)
;Regs 14-15 : CursorPos-Adress (Hi und Lo)
;ModeReg: x x x x x x x x
; 2nd Page ┘ │ │ │ │ │ │ └ ?
; ? ┘ │ │ │ │ └ Hires
; Blink enable ┘ │ │ └ ?
; ? ┘ └ Display Enable
;StatusReg: x x x x x x x x
; Vert retr ┘ │ │ │ │ │ │ └ Horiz retr
; ? ┘ │ │ │ │ └ ?
; ? ┘ │ │ └ ?
; ? ┘ └ Cur Pixel
;ConfigReg: x x x x x x x x
; ? ─┴─┴─┴─┴──┴─┘ │ └ Enable Graphic Mode
; └ Enable 2nd Page
;=============================================================================
BiosData SEGMENT AT 40h
ORG 49h
CurVideoMode DB ?
NrOfColumns DW ?
BytesPerPage DW ?
CurDispStart DW ?
CursPosTable DW 8 DUP (?)
CurCursShape DW ?
ActivePage DB ?
VidCtrlIoAdr DW ?
ModeRegCont DB ?
ORG 78h
TimeOutTable DB 4 DUP (?)
ORG 84h
LastTextLine DB ?
BiosData ENDS
TheProgram SEGMENT BYTE PUBLIC
ASSUME CS:TheProgram,DS:BiosData,ES:NOTHING,SS:NOTHING
ORG 100h
EntryPoint PROC NEAR
jmp Main
DB 13,'(c)Stressi',26
EntryPoint ENDP
ORG 10Fh
PrnInfo LABEL BYTE ;Ab hier stehen die Drucker-Daten für "PrtScr":
PrinterNr DB 0 ;Zuerst die Nummer des zu benutzenden Druckers,
PrnTextInit DB 2,13,10 ;dann die Strings, die vor bzw nach
DB 16-($-PrnTextInit) DUP(-1)
PrnTextExit DB 0 ;einer Text-HardCopy gesendet werden,
DB 16-($-PrnTextExit) DUP(-1)
PrnGraphInit DB 2,13,10 ;die Strings, die vor bzw nach
DB 16-($-PrnGraphInit) DUP(-1)
PrnGraphExit DB 2,10,10 ;einer Graph-HardCopy gesendet werden
DB 16-($-PrnGraphExit) DUP(-1)
PrnLineStart DB 4,27,'L',LOW NrOfDotsX,HIGH NrOfDotsX ;sowie die, die vor
DB 16-($-PrnLineStart) DUP(-1)
PrnLineEnd DB 4,13,27,'J',24 ;bzw nach jeder Graph-Zeile gesendet werden.
DB 16-($-PrnLineEnd) DUP(-1)
PrnInfoSize EQU $-OFFSET PrinterNr
ReadLightpen EQU NotImplemented
ColorPalette EQU NotImplemented
Reserved EQU NotImplemented
FuncTable DW SetVideoMode,SetCursorShape,SetCursorPos,ReadCursorPos
DW ReadLightpen,SelActivePage,ScrollUp,ScrollDown
DW ReadAttrChar,WriteAttrChar,WriteCharOnly,ColorPalette
DW WriteDot,ReadDot,TeletypeOut,GetVideoState,Reserved
DW Reserved,Reserved,WriteString
GraphModeTiming DB 54,45,47, 7,95,0,90,90,2, 3, 0, 0,0,0 ;14 Bytes...
TextModeTiming DB 97,80,82,15,25,6,25,25,2,13,11,12,0,0
DispStartTable DW ?,? ;ByteOfs für je eine Page,
PageSegment DW Page0Seg,Page1Seg ;bezogen auf diese Segmente.
FastScrollFlag DB Dunnow
PrtScrActive DB False
Int10Active DB False
SavedInt10Vec DW ?,?
SavedInt5Vec DW ?,?
SavedInt10Stack DW ?,?
SavedInt5Stack DW ?,?
PrtScr8x8Buffer DB 8,8 DUP (?) ;zur Konvertierung: In der BitMap liegen
;die Bits waagerecht, der Drucker braucht sie senkrecht.
GetPageArrayIndex MACRO ArrayBase,DestReg ;ZF=PageNr.
LOCAL IsPage0
mov DestReg,ArrayBase
jz IsPage0
inc DestReg
inc DestReg
IsPage0:
ENDM
DisplayAdress_Text MACRO XPos,YPos
;XPos und YPos dürfen weder AL noch AH sein !!!
mov al,80
mul YPos
add al,XPos
adc ah,0
ENDM ;AX = WordAdress im VideoRam.
DisplayAdress_Graph MACRO XPos,YPos ;XPos in Chars, YPos in Scans.
;XPos und YPos dürfen weder AL noch AH sein !!!
mov al,90 ;Eine ScanLine ist 90 Bytes lang,
mul YPos ;ein Char ist 2 Scans hoch.
add al,XPos
adc ah,0
ENDM ;AX = ByteAdress im VideoRam.
ZeichenSatz8x8 LABEL BYTE
INCLUDE Herkules.INC
NewInt10Handler PROC FAR
jmp SHORT HandleInterrupt
DB 'Herkules V1.0 ' ;müssen 16 Zeichen sein (Konvention...)
HandleInterrupt:
cmp cs:Int10Active,True ;"Herkules" schon mal aufgerufen?
je NotAvailable ;Wir haben aber nur EINEN Stack!
cmp ah,19 ;FunctionNr>19?
ja NotAvailable ;Wird von uns nicht unterstützt.
push si
mov cs:Int10Active,True ;Flag setzen, daß jetzt auf den
mov cs:SavedInt10Stack,sp ;internen Stack umgeschaltet wird:
mov cs:SavedInt10Stack+2,ss
mov si,cs ;Alten StackPointer merken
mov ss,si ;und auf neuen Stack umschalten
mov sp,OFFSET MyInt10Stack+StackSize ;(SP auf das ENDE des Stacks!).
push ds
push ax
mov si,BiosData ;DS auf das Bios-Segment setzen.
mov ds,si
xchg al,ah
mov si,ax ;FunctionNr von AH
xchg al,ah ;nach SI und von
and si,00FFh ;Byte nach Word wandeln.
jz ThatConcernsMe ;Function 0? => In Ordnung.
mov ah,CurVideoMode
cmp ah,7 ;Mode 7 (HercText)
je ThatConcernsMe
cmp ah,8 ;oder 8 (HercGraf)?
jne CallOldBios
ThatConcernsMe: ;Dann geht das uns etwas an:
sti ;Interrupts enablen,
shl si,1
call cs:[FuncTable+si] ;Behandlungs-Routine aufrufen
pop ax ;mit AH=CurVideoMode.
pop ds
mov ss,cs:SavedInt10Stack+2
mov sp,cs:SavedInt10Stack ;Auf alten Stack zurückschalten und
mov cs:Int10Active,False ;entsprechendes Flag wieder löschen,
pop si ;Register restaurieren
iret ;und zurück.
NotAvailable:
jmp dword ptr cs:[SavedInt10Vec]
CallOldBios:
call NotImplemented ;Subroutine kommt NICHT zurück...
NewInt10Handler ENDP
SelActivePage PROC NEAR ;AL=new PageNr
and al,1 ;Es gibt nur zwei Pages...
mov ActivePage,al ;PageNr im BiosSeg speichern.
GetPageArrayIndex 0,si
push dx ;DX darf nicht verändert werden!
mov dx,ModeReg
mov al,ModeRegCont ;Inhalt des ModeReg aus BiosSeg lesen
and al,7Fh ;Bit für "2nd Page" löschen.
test ActivePage,1 ;Ist es denn wirklich die erste Page?
jz SetModeReg ;Dann neuen Wert schreiben.
or al,80h ;Sonst Bit für "2nd Page" vorher setzen.
SetModeReg:
mov ModeRegCont,al ;Neuen Wert a) ins BiosSeg schreiben
out dx,al ;und b) an den VideoChip schicken.
pop dx
mov ax,[CursPosTable+si]
call UpdateCursorPos ;CursorPos der neuen Page an VideoChip schicken,
mov ax,cs:[DispStartTable+si]
jmp UpdateDispStart ;ebenso ihren DispStart.
SelActivePage ENDP ;AX and SI destroyed.
SetCursorShape PROC NEAR ;CH=CursorStart, CL=CursorEnd
push dx
mov CurCursShape,cx ;Neue "CursorShape" im BiosSeg eintragen.
mov dx,IndexReg
mov al,11
out dx,al ;RegisterNr ins IndexReg
inc dx
mov al,cl
out dx,al ;LowByte ins DataReg.
dec dx
mov al,10 ;HighByte: Zuerst RegNr
out dx,al ;ins IndexReg
inc dx
mov al,ch ;und dann das DatenByte
out dx,al ;ins DataReg.
pop dx
ret
SetCursorShape ENDP ;AX destroyed.
SetCursorPos PROC NEAR ;BH=PageNr, (DL/DH)=new CursorPos, AH=CurVideoMode
mov al,bh
and al,1 ;Mehr als 2 Seiten gibt's sowieso nicht.
GetPageArrayIndex OFFSET(CursPosTable),si
mov [si],dx ;Neue Cursor Position im BiosSeg eintragen.
cmp al,ActivePage ;Wurde die CursorPos der active Page geändert
jne SetCursPosDone
cmp ah,7 ;und ist auch noch der Text-Mode aktiv?
jne SetCursPosDone
mov ax,dx ;Dann muß die neue Position
call UpdateCursorPos ;auch dem 6845 mitgeteilt werden.
SetCursPosDone:
ret
SetCursorPos ENDP ;AX and SI destroyed.
ReadCursorPos PROC NEAR ;BH=PageNr
test bh,1
GetPageArrayIndex OFFSET(CursPosTable),si
mov dx,[si]
mov cx,CurCursShape
ret
ReadCursorPos ENDP ;SI destroyed, CX=CursorShape, DX=CursorPos.
ScrollUp PROC NEAR
;AL=NrOfLines, (CH/CL)-(DH/DL), BH=NewAttribute, AH=CurVideoMode.
cmp ch,LastTextLine
ja InvalidWindow
cmp dh,ch ;LowerCorner<=UpperCorner?
jb InvalidWindow ;Da tun wir am besten gar nix,
cmp dl,cl ;ebenso bei RightCorner<=LeftCorner.
jb InvalidWindow
push bx
mov bl,bh
mov bh,ActivePage
call ScrollPartOfScreenUp
pop bx
InvalidWindow:
ret
ScrollUp ENDP ;AX and SI destroyed.
ScrollDown PROC NEAR
;AL=NrOfLines, (CH/CL)-(DH/DL), BH=NewAttribute, AH=CurVideoMode.
cmp ch,LastTextLine
ja InvalidWindow
cmp dh,ch ;LowerCorner<=UpperCorner?
jb InvalidWindow ;Da tun wir am besten gar nix,
cmp dl,cl ;ebenso bei RightCorner<=LeftCorner.
jb InvalidWindow
push bx
mov bl,bh
mov bh,ActivePage
call ScrollPartOfScreenDown
pop bx
ret
ScrollDown ENDP
ReadAttrChar PROC NEAR ;BH=PageNr, AH=CurVideoMode
push dx ;Register retten.
push di
push es
test bh,1
GetPageArrayIndex 0,di ;Welche von beiden Pages meint der?
mov dx,[CursPosTable+di] ;Entsprechende CursorPos
mov es,cs:[PageSegment+di] ;und PageSegment holen.
cld
cmp ah,8
je ReadGraphChar ;GraphMode? Dann andere Routine benutzen.
DisplayAdress_Text dl,dh ;CursorPos in
shl ax,1 ;Adresse umrechnen
mov di,ax
mov ax,es:[di] ; => dort steht das Attr und der Char.
jmp SHORT ReadCharDone ;Das war's eigentlich schon,
ReadGraphChar: ;im GraphMode ist das viel komplizierter:
push cx
shl dh,1 ;Cursor YPos von "CharLines" nach "Scans" wandeln
DisplayAdress_Graph dl,dh ;und in eine Adresse umrechnen, die dann,
mov di,cs:[DispStartTable+di] ;zum DispStart addiert und
add di,ax ;auf eine BitMap begrenzt,
and di,1FFFh ;auf das Graphik-Pattern zeigt.
mov ah,0FFh ;XOR-Flag auf "revers" setzen.
SearchAgain:
mov al,es:[di] ;Das erste Byte des Graphik-Patterns holen
xor al,ah ;und mit dem XOR-Flag verknüpfen.
mov si,OFFSET ZeichenSatz8x8
mov cx,256
sub si,8 ;Dieses erste Byte nacheinander mit dem
CompFirstByte: ;jeweils ersten Byte der 256 Zeichen
add si,8 ;(komplette ASCII-Tabelle)
cmp al,cs:[si] ;des ZeichenSatzes vergleichen.
loopne CompFirstByte
jne UnknownChar
push si ;Weitertesten und die gesamten
push di ;8 Bytes Graphik-Pattern
mov dl,2 ;(gleich 2 Scans) vergleichen.
CompRestOfChar:
lods byte ptr cs:[si] ;Nächstes Byte aus dem ZeichenSatz holen,
xor al,ah ;über das XOR-Flag verknüpfen
cmp al,es:[di] ;und mit dem nächsten Graphik-Byte vergleichen.
jne CharCompared
add di,2000h ;Gleich? Dann eine Zeile tiefer (= nächste BitMap)
jno CompRestOfChar ;es sei denn, das war schon die letzte.
add di,90 ;In diesem Fall Zeiger einen Scan tiefer setzen.
and di,1FFFh ;Dabei PageWrap beachten.
dec dl ;War das der letze Scan?
jnz CompRestOfChar ;Nicht? Dann weitervergleichen.
CharCompared:
pop di
pop si ;Vergleich beendet.
je GraphCharRecognized ;Wie sah denn das Ergebnis aus, gleich
mov al,es:[di] ;oder ungleich?
xor al,ah
test cx,cx ;Ungleich? Dann wieder nach erstem Byte suchen, aber
jnz CompFirstByte ;nur wenn Zeichensatz noch nicht zuende durchsucht.
UnknownChar:
inc ah ;Zeichen nicht erkannt? Dann nochmal mit neuem XOR-Flag pro-
jz SearchAgain ;bieren, wenn es nicht schon alle Werte angenommen hatte.
GraphCharRecognized:
mov al,255
sub al,cl
pop cx
mov ah,07h
test dl,dl ;Zeichen gefunden. Wie stand denn das XOR-Flag?
jz ReadCharDone ;Auf reverse?
mov ah,70h ;Dann entsprechendes Attribute zurückgeben.
ReadCharDone: ;AL = Char, AH = Attribute.
pop es
pop di ;Register zurückholen
pop dx
pop si ;und ReturnAdresse kurz lupfen.
add sp,2 ;Dann kann ich nämlich den gepushten AX vom Stack entfernen
push ax ;und statt dessen den neuen dorthin legen.
push si ;Jetzt nur noch die alte ReturnAdresse
ret ;wieder zurück und wir sind fertig.
ReadAttrChar ENDP ;AX and SI destroyed.
WriteCharOnly PROC NEAR
;BH=PageNr, CX=NrOfChars, AL=CharToWrite, AH=CurVideoMode
cmp ah,8 ;Ist Graphik-Mode aktiv? Im GraphMode ist es nicht
mov bl,7 ;möglich, NUR ein Zeichen OHNE Attribute zu schreiben.
je WriteAttrChar ;Statt dessen "WriteAttrChar" mit Attr=7 benutzen.
push cx ;Also TextModus. Dann Register retten.
push dx
push di
push es
jcxz WriteCharDone ;Gar nichts zum Schreiben da? Dann Abbruch.
cld
push ax ;CharToWrite zwischenspeichern.
test bh,1 ;Welche Page denn?
GetPageArrayIndex 0,di ;Ach so, DIE...
mov dx,[CursPosTable+di]
mov es,cs:[PageSegment+di]
DisplayAdress_Text dl,dh ;CursorPos in Adresse umrechnen,
shl ax,1 ;nach "ByteOffset" wandeln
mov di,ax ;ergibt ES:DI = CharAdress.
pop ax
WriteCharOnlyLoop: ;Zeichen ins VideoRam donnern:
stosb ;Character-Byte schreiben
inc di ;aber Attribute-Byte überspringen.
loop WriteCharOnlyLoop ;Noch Zeichen da?
WriteCharDone:
pop es
pop di
pop dx
pop cx
ret
WriteCharOnly ENDP ;AX and SI destroyed.
WriteAttrChar PROC NEAR
;BH=PageNr, CX=NrOfChars, AL=Char, BL=Attribute, AH=CurVideoMode
push cx
push dx ;Register retten
push di
push es
jcxz WriteCharDone ;Ist überhaupt etwas da zum Schreiben?
cld
test bh,1
GetPageArrayIndex 0,di ;Um welche Page handelt es sich?
mov dx,[CursPosTable+di] ;Entsprechende "CursorPos"
mov es,cs:[PageSegment+di] ;und "PageSegment" holen.
cmp ah,8 ;Sind wir im Graphik-Modus?
je WriteGraphChar ;Dann andere Routine benutzen.
push ax ;CharToWrite zwischenspeichern.
DisplayAdress_Text dl,dh ;CursorPos in Adresse umrechnen,
shl ax,1 ;nach "ByteOffset" wandeln
mov di,ax ;ergibt ES:DI = CharAdress.
pop ax
mov ah,bl
rep stosw ;Zeichen ins VideoRam donnern
jmp SHORT WriteCharDone ;und fertig.
WriteGraphChar:
push bx ;Im Graphik-Mode ist alles viel komplizierter:
push ax ;CharToWrite zwischenspeichern.
shl dh,1 ;DH von "CharLines" nach "Scans" wandeln
DisplayAdress_Graph dl,dh ;und daraus die Adresse berechnen,
mov di,cs:[DispStartTable+di] ;außerdem den DispStart
add di,ax ;nicht vergessen
and di,1FFFh ;und begrenzen.
mov dh,0 ;AND-Flag und
mov bh,0 ;XOR-Flag löschen.
test bl,70h ;Soll das Zeichen revers erscheinen?
jz NotReverse
dec bh ;Dann XOR-Flag setzen. Soll das Zeichen
test bl,07h ;revers & white (=> weiß auf weiß) erscheinen?
jnz GotFlags ;Dann stimmt das mit dem gelöschte AND-Flag.
SetAndFlag:
dec dh ;Sonst AND-Flag setzen,
jmp SHORT GotFlags ;dann stimmen die Flags.
NotReverse:
test bl,07h ;Soll das Zeichen black & non revers (=> schwarz auf
jnz SetAndFlag ;schwarz) erscheinen? Dann auch erst AND-Flag setzen.
GotFlags:
mov si,OFFSET ZeichenSatz8x8
pop ax
mov ah,0 ;ASCII-Code des Zeichens auf 16Bit erweitern
shl ax,1 ;und 3x nach links schieben
shl ax,1 ;(entspricht *8)
shl ax,1 ;ergibt den Offset
add si,ax ;in die ZeichenSatz-Tabelle.
mov bl,2 ;2 komplette Scans (gleich 8 Zeilen)
mov dl,cl ;zu je CL Zeichen (gleich Bytes) schreiben.
mov ax,es
WriteNextLineOfChars:
mov es,ax
ComputeWrapPos di
push di ;ZeilenAnfang für später merken.
lods byte ptr cs:[si] ;So: Eine Zeile aus dem ZeichenSatz holen,
and al,dh ;mit den Flags verknüpfen
xor al,bh ;und soweit schreiben,
rep stosb ;wie ohne 8k-Wrap möglich ist.
xor di,di ;Dann Zeiger auf den PageAnfang zurücksetzen
mov cl,ah ;und auch den Rest schreiben.
rep stosb
pop di
mov ax,es ;Jetzt ES auf nächste BitMap setzen
add ah,02h ;(=eine Bildschirmzeile tiefer).
cmp ah,HIGH(Page0Seg)+8
je PrepareNextScan
cmp ah,HIGH(Page1Seg)+8
jb WriteNextLineOfChars ;War das etwa die letzte BitMap?
PrepareNextScan:
sub ah,08h ;Dann Zeiger wieder auf die erste BitMap setzen
add di,90 ;aber einen Scan tiefer.
and di,1FFFh
dec bl ;War das der letzte?
jnz WriteNextLineOfChars ;Nicht? Dann weitermachen.
pop bx
pop es
pop di
pop dx
pop cx
ret
WriteAttrChar ENDP ;AX and SI destroyed.
WriteDot PROC NEAR ;CX=XPos, DX=YPos, AL=ColorNr, AH=CurVideoMode
push bx
push cx ;Register retten.
push dx
push es
cmp ah,8 ;Sind wir auch wirklich im Graphik-Modus?
jne WriteDotDone ;Im TextModus gibt es keine Punkte zum Setzen.
push ax ;ColorByte merken.
test ActivePage,1
call ComputeDotAdress ;Adresse des Punktes
mov al,80h ;und seine Maske
shr al,cl ;berechnen.
mov ah,al
xor ah,0FFh ;Byte invertieren als Lösch-Maske.
pop dx ;ColorByte zurückholen
test dl,7Fh ;und testen:
jnz MakeDotWhite ;Soll der Punkt schwarz oder
mov al,0 ;weiß werden?
MakeDotWhite:
test dl,dl ;Soll der Punkt entsprechend gesetzt
js InvertDot ;oder invertiert werden?
and es:[bx],ah ;Punkt erst mal löschen (schwarz setzen)
or es:[bx],al ;und dann bei Bedarf wieder setzen,
jmp SHORT WriteDotDone ;das war's.
InvertDot: ;Punkt invertieren?
xor es:[bx],al ;Dann Byte mit der Maske verknüpfen.
WriteDotDone:
pop es
pop dx
pop cx
pop bx
ret
WriteDot ENDP ;AX destroyed.
ReadDot PROC NEAR ;CX=XPos, DX=YPos.
push bx
push cx
push dx
push es
test ActivePage,1 ;Welche Page ist gerade aktiv?
call ComputeDotAdress
mov al,80h ;Maske aus der Nummer
shr al,cl ;des Bits bestimmen.
test es:[bx],al ;Ist das Bit gelöscht?
mov al,0 ;Dann "black" zurückmelden,
jz DotIsBlack
mov al,7 ;sonst "white".
DotIsBlack:
pop es
pop dx
pop cx ;Register wieder holen
pop bx
pop si ;und ReturnAdresse kurz lupfen.
add sp,2 ;Dann kann ich nämlich den gepushten AX vom Stack entfernen
push ax ;und statt dessen den neuen dorthin legen.
push si ;Jetzt nur noch die alte ReturnAdresse
ret ;wieder zurück und wir sind fertig.
ReadDot ENDP ;AX and SI destroyed.
TeletypeOut PROC NEAR
;AL=CharToWrite, BL=ForegroundColor in GraphMode, AH=CurVideoMode
push bx
mov bh,ActivePage
cmp ah,8 ;In welchem VideoMode sind wir gerade?
je GraphTeletype
mov bl,al ;TextMode? Dann zu schreibendes Zeichen merken,
call ReadAttrChar ;Zeichen+Attribute an CursorPos holen
mov al,bl ;und das neue Zeichen mit dem
mov bl,ah ;Attribute des alten schreiben.
GraphTeletype:
test bh,1 ;In welcher Page sind wir gerade?
GetPageArrayIndex OFFSET(CursPosTable),si
call WriteOneChar ;So, jetzt das Zeichen MITSAMT Attribute schreiben
call UpdateCursorPos ;und neue CursorPos an den 6845 schicken.
pop bx
ret
TeletypeOut ENDP ;AX and SI destroyed.
GetVideoState PROC NEAR
mov bh,ActivePage
pop ax ;ReturnAdresse kurz lupfen. Dann kann ich
add sp,2 ;nämlich den gepushten AX vom Stack entfernen
push word ptr CurVideoMode ;und statt dessen den neuen Wert dorthin legen.
push ax ;Jetzt nur noch die alte ReturnAdresse
ret ;wieder zurück und wir sind fertig.
GetVideoState ENDP ;AX destroyed, BH=ActivePage.
WriteString PROC NEAR
;[ES:BP]=String, CX=NrOfChars, DX=CursStartPos, BH=PageNr, AL=SubModeNr
;AH=CurVideoMode, BL=Attribute in some SubModes.
push bp
test al,1 ;Wie sieht das LSB der SubMode-Nr aus?
pushf ;Zustand für später merken!
test bh,1 ;In welcher Page schreiben?
GetPageArrayIndex OFFSET(CursPosTable),si
push [si] ;Entsprechende CursorPos merken
mov [si],dx ;und StartPos des Strings als CursPos speichern.
jcxz EndOfString
and al,2 ;SubMode 0 oder 1? Dann enthält BL
jz CommonAttribute ;das Attribute für den gesamten String.
push bx
IndividualAttributes: ;Sonst besteht der String aus Zeichen UND Attibutes:
mov ax,es:[bp] ;Davon die erste Char-Attr-Kombination lesen
inc bp ;und Zeiger vorerst ein Byte weitersetzen.
cmp al,7
je NotPrintable
cmp al,8 ;Jetzt mal sehen:
je NotPrintable
cmp al,10 ;Ist das Zeichen weder BEL
je NotPrintable
cmp al,13 ;noch BS noch CR noch LF?
je NotPrintable ;Dann ist das ein printable Char mit einem Attrib-Byte
inc bp ;dahinter, der Zeiger muß entsprechend korrigiert werden.
NotPrintable:
mov bl,ah
call WriteOneChar ;mit Attr in BL schreiben
loop IndividualAttributes ;und weiter.
pop bx
jmp SHORT EndOfString
CommonAttribute:
mov al,es:[bp] ;Erstes Zeichen des Strings lesen
call WriteOneChar ;und mit BL=Attr schreiben.
inc bp ;Zeiger weiter und
loop CommonAttribute ;nochmal von Vorne.
EndOfString:
pop ax ;Alte CursorPos VOR dem Schreiben zurückholen,
popf ;dann Flags testen:
pop bp
jz DontMoveCursor ;Sollte der Cursor bewegt werden oder nicht?
mov ax,[si] ;Ja? Dann neue Position an 6845 schicken.
DontMoveCursor:
mov [si],ax ;Sonst alte Position wieder speichern
jmp UpdateCursorPos ;und an 6845 schicken.
WriteString ENDP ;AX and SI destroyed.
NotImplemented PROC NEAR
pop ax ;RückkehrAdresse nach "NewInt10Handler" verwerfen,
pop ax
pop ds ;gerettete Register wiederholen,
mov ss,cs:SavedInt10Stack+2
mov sp,cs:SavedInt10Stack ;auf alten Stack zurückschalten und
mov cs:Int10Active,False ;entsprechendes Flag wieder löschen,
pop si ;dann in den alten
jmp dword ptr cs:[SavedInt10Vec] ;Int10-Handler springen.
NotImplemented ENDP
SetVideoMode PROC NEAR ;AL=ModeNr
cli ;Interrupts erst mal wieder verhindern!
cmp al,7 ;Soll Modus 7 gesetzt werden?
je HerkGrafAus ;Machen wir doch.
cmp al,8
jne NotImplemented ;Weder 7 noch 8? Ha'm wa' nich.
push dx ;Also Modus 8 setzen?
mov CurVideoMode,al ;"VideoMode" im BiosSeg speichern.
mov dx,8000h
mov al,HGC_Full
mov ah,00101011b ;1st Page, Blinker enable, Display enable, Hires
mov si,OFFSET GraphModeTiming
jmp SHORT UpdateVideoMode
HerkGrafAus:
push dx ;Also Modus 7 setzen?
mov CurVideoMode,al ;"VideoMode" im BiosSeg speichern.
mov dx,1000h
mov al,HGC_Text
mov ah,00101001b ;1st Page, Blinker enable, Display enable, Text
mov si,OFFSET TextModeTiming
UpdateVideoMode:
;[CS:SI]=Timing-Daten, AH=ModeReg-Inhalt, AL=Configuration, DX=BytesPerPage
mov BytesPerPage,dx
mov dx,ConfigReg
out dx,al ;Configuration setzen.
mov dx,StatusReg
NotYetSync:
in al,dx
test al,80h
jnz NotYetSync ;Auf Vertical Retrace warten.
mov dx,ModeReg
mov al,ah
out dx,al ;Video-Modus umschalten,
mov ModeRegCont,al ;Inhalt des ModeReg im BiosSegment merken
mov ah,0
mov dx,IndexReg ;und die Timing-Daten in einer
OutStringByteLoop: ;Schleife in den Chip schieben.
mov al,ah
out dx,al ;Zuerst die Nummer des Registers
inc dx ;ins IndexReg,
lods byte ptr cs:[si] ;dann das nächste Timing-Byte holen
out dx,al ;und ins DataReg schreiben.
dec dx
inc ah
cmp ah,14
jne OutStringByteLoop ;Noch weitere Timing-Daten?
xor ax,ax
mov CursPosTable+0,ax ;Cursor nach (0/0)
mov CursPosTable+2,ax ;in Page 0 und 1,
mov cs:DispStartTable+0,ax ;außerdem
mov cs:DispStartTable+2,ax ;"DispStart" zurücksetzen.
call SelActivePage ;Page 0 selektieren und vor dem langen
sti ;Bildschirm-Löschen Interrupts enablen.
mov dx,256*25+80 ;Bildschirm enthält 80x25,
cmp CurVideoMode,8
jne SmallScreen
mov dx,256*NrOfLines+NrOfChars ;im Graphik-Modus 90x45 Zeichen.
SmallScreen:
mov byte ptr NrOfColumns+1,0
mov byte ptr NrOfColumns,dl ;Noch ein paar Variablen
dec dh
mov LastTextLine,dh ;im Bios-Segment setzen.
inc dh
push bx
push cx
mov bh,0 ;Bildschirm in Page 0
mov bl,7 ;mit Attribute 7
mov cx,0
call ClearWindow ;löschen,
inc bh ;ebenso in Page 1.
mov cx,0
mov dl,byte ptr NrOfColumns
mov dh,LastTextLine
inc dh
call ClearWindow
mov cx,0C0Dh
call SetCursorShape
pop cx
pop bx
pop dx
ret
SetVideoMode ENDP ;AX and SI destroyed.
NewInt5Handler PROC FAR
push ax ;Register retten.
push ds
cmp cs:PrtScrActive,True ;Wird gerade schon eine HardCopy gemacht?
je PrtScrAborted ;Dann diese abbrechen,
mov ax,BiosData
mov ds,ax ;sonst DS auf das BiosSegment
mov ah,CurVideoMode ;setzen und von dort VideoModus lesen.
cmp ah,7
je SupportedMode ;Sind wir im Herc-TextMode
cmp ah,8 ;oder im Herc-GraphMode?
je SupportedMode ;Dann geht das uns an,
pop ds ;im anderen Fall Register
pop ax ;wieder herstellen und an
jmp dword ptr cs:[SavedInt5Vec] ;alten PrtScr-Handler übergeben.
SupportedMode:
mov cs:PrtScrActive,True ;Jetzt geht's gleich los.
cmp ah,7 ;In welchem VideoModus sind wir?
mov cs:SavedInt5Stack,sp
mov cs:SavedInt5Stack+2,ss
mov ax,cs ;Entsprechendes Flag schon mal setzen
mov ss,ax ;und auf eigenen Stack umschalten
mov sp,OFFSET MyInt5Stack+StackSize ;(SP auf das ENDE des Bereichs!).
mov al,ActivePage
sti ;Dann kann ich jetzt auch die Interrupts enablen.
je MakeTextHardcopy
call PrintGraphScreen ;TextModus? Dann Drucken.
jmp SHORT PrtScrDone
MakeTextHardcopy:
call PrintTextScreen ;GraphModus? Dann andere Druck-Routine benutzen.
PrtScrDone:
mov ss,cs:SavedInt5Stack+2
mov sp,cs:SavedInt5Stack ;Auf alten Stack zurückschalten
PrtScrAborted:
mov cs:PrtScrActive,False
pop ds
pop ax ;und Register wiederholen.
iret
NewInt5Handler ENDP
IF Debugging
;----------------------------------------------------------------------------+
BreakPoint PROC NEAR ;!
pushf ;!
push ax ;!
push dx ;!
push si ;!
mov dx,ModeReg ;!
mov al,00101001b ;!
out dx,al ;Video-Modus umschalten, ;!
mov si,OFFSET TextModeTiming ;!
mov ah,0 ;!
mov dx,IndexReg ;und die Timing-Daten in einer ;!
cld ;!
OutStringLoop0: ;Schleife in den Chip schieben. ;!
mov al,ah ;!
out dx,al ;Zuerst die Nummer des Registers ;!
inc dx ;ins IndexReg, ;!
lods byte ptr cs:[si] ;dann das nächste Timing-Byte holen ;!
out dx,al ;und ins DataReg schreiben. ;!
dec dx ;!
inc ah ;!
cmp ah,14 ;!
jne OutStringLoop0 ;Noch weitere Timing-Daten? ;!
pop si ;!
pop dx ;!
pop ax ;!
popf ;!
int 3 ;!
pushf ;!
push ax ;!
push dx ;!
push si ;!
mov dx,ModeReg ;!
mov al,00101011b ;!
out dx,al ;!
mov si,OFFSET GraphModeTiming ;!
mov ah,0 ;!
mov dx,IndexReg ;und die Timing-Daten in einer ;!
cld ;!
OutStringLoop1: ;Schleife in den Chip schieben. ;!
mov al,ah ;!
out dx,al ;Zuerst die Nummer des Registers ;!
inc dx ;ins IndexReg, ;!
lods byte ptr cs:[si] ;dann das nächste Timing-Byte holen ;!
out dx,al ;und ins DataReg schreiben. ;!
dec dx ;!
inc ah ;!
cmp ah,14 ;!
jne OutStringLoop1 ;Noch weitere Timing-Daten? ;!
pop si ;!
pop dx ;!
pop ax ;!
popf ;!
ret ;!
BreakPoint ENDP ;!
;!
MarkOn PROC NEAR ;!
push es ;!
push ax ;!
mov al,23h ;!
out 61h,al ;!
mov ax,0B000h ;!
mov es,ax ;!
pop ax ;!
mov byte ptr es:[159],7 ;!
inc byte ptr es:[158] ;!
pop es ;!
ret ;!
MarkOn ENDP ;!
;!
MarkOff PROC NEAR ;!
push es ;!
push ax ;!
mov al,20h ;!
out 61h,al ;!
mov ax,0B000h ;!
mov es,ax ;!
pop ax ;!
mov byte ptr es:[159],7 ;!
dec byte ptr es:[158] ;!
pop es ;!
ret ;!
MarkOff ENDP ;!
;----------------------------------------------------------------------------+
ENDIF
;+---------------------------------------------------------------------------+
;! Der Teil ab hier wird nach dem Installieren wieder freigegeben. !
;+---------------------------------------------------------------------------+
ASSUME CS:TheProgram,DS:TheProgram,ES:TheProgram,SS:TheProgram
RotateBuffer LABEL BYTE
MyInt10Stack EQU RotateBuffer+BufSize
MyInt5Stack EQU MyInt10Stack+StackSize
EndOfProgram EQU MyInt5Stack+StackSize
Card_Unknown EQU 0
Card_Mono EQU 1
Card_Herkules EQU 2
VideoCard DB ?
VideoMode DB ?
ExplainFlag DB LOW False
WhenTimeOut DW ?,?
Main PROC NEAR
call CheckHardware
mov VideoCard,al
mov VideoMode,ah
mov si,80h ;Ab [PSP:80h] befinden sich die Aufruf-Parameter.
call ProcessCommandLine
cmp ExplainFlag,True ;Sollen nur Infos ausgegeben werden?
je GetInfos
jmp Initialisation
Main ENDP ;Dann Fall-Through nach:
GetInfos PROC NEAR
mov dx,OFFSET ExplainMsg ;Erklärungs-Message ausgeben.
mov ah,9 ;FunctionCall PRINT STRING ;Sie endet mit den Worten:
int 21h ;"Checking Hardware:"...
mov ah,Ret_Expl
mov al,VideoCard
cmp al,Card_Unknown
mov dx,OFFSET UnknownMsg ;Mal sehen: Was für eine
je MessageAndStop ;Graphik-Karte haben wir
cmp al,Card_Mono ;hier denn?
mov dx,OFFSET MonoCardMsg
je MessageAndStop
mov ah,9 ;FunctionCall PRINT STRING ;Eine Herkules? Dann
mov dx,OFFSET HerkCardMsg ;testen wir weiter:
int 21h
call TestIfResident ;Ist dieses Programm
mov ah,Ret_Expl ;schon resident?
mov dx,OFFSET NotResiMsg
jne MessageAndStop ;Nein, noch nicht.
mov dx,OFFSET ResidentMsg
mov ah,9 ;FunctionCall PRINT STRING
int 21h
mov ah,Ret_Expl
mov dx,OFFSET OnMsg
cmp es:FastScrollFlag,True
je MessageAndStop
mov dx,OFFSET OffMsg
GetInfos ENDP ;Schon wieder Fall-Through:
MessageAndStop PROC NEAR ;[DS:DX]=Message, AH=ReturnCode.
push ax
mov ah,9 ;FunctionCall PRINT STRING
int 21h
pop ax
mov al,ah
mov ah,4Ch ;FunctionCall TERMINATE PROCESS
int 21h
MessageAndStop ENDP
RemoveFromMemory PROC NEAR ;[DX:0]=RotateBuffer, [ES:0]=residentes HERKULES.
cmp VideoMode,8 ;Sind wir z.Z. im Graphik-Modus?
jne LegalVidMode
mov ah,0
mov al,7 ;Dann auf jeden Fall in den Text-Modus zurück-
int 10h ;schalten, sonst stehen wir nacher im Dunkeln.
LegalVidMode:
mov ah,5 ;Das tun wir auch, wenn
mov al,0 ;jetzt gerade die zweite
int 10h ;Page aktiv ist.
mov ah,25h ;FunctionCall SET VECTOR
lds dx,dword ptr es:SavedInt5Vec ;Int5 (PrtScr)-Vektor
mov al,5h ;auf alten Bios-Treiber
int 21h ;zurückbiegen,
mov ah,25h ;FunctionCall SET VECTOR
lds dx,dword ptr es:SavedInt10Vec ;ebenso Int10-Vektor.
mov al,10h
int 21h
mov dx,ConfigReg ;Ach ja, man sollte die Konfiguration
mov al,HGC_Diag ;auch noch auf "DIAG" stellen, damit
out dx,al ;die Karte wieder MGA-kompatibel ist.
nop
push es:[2Ch] ;Adresse des Environment-Segments merken.
mov ah,49h ;FunctionCall FREE MEMORY ;Speicher des residenten
int 21h ;HERKULES' freigeben.
pop es
mov ah,49h ;FunctionCall FREE MEMORY ;Speicher des Environment-
int 21h ;Segments freigeben.
mov ah,35h ;FunctionCall GET VECTOR
mov al,1Fh
int 21h ;Wohin zeigt der Vektor $1F
cmp bx,OFFSET ZeichenSatz8x8 ;(der Zeiger auf die obere Hälfte
jne GrafTablNotLoaded ;des IBM-ASCII-Zeichensatzes
mov ax,es ;für CGA-GraphikModi)?
mov dx,cs
cmp ax,dx ;Auf unseren eingebauten ZeichenSatz?
jne GrafTablNotLoaded
xor dx,dx ;Dann diesen Vektor
mov ds,dx ;auf NIL zurücksetzen,
mov ah,25 ;FunctionCall SET VECTOR ;sonst gibt der nacher noch
mov al,1Fh ;Zeichen aus dem ZeichenSatz aus,
int 21h ;der schon gar nicht mehr existiert.
GrafTablNotLoaded:
mov ah,Ret_Ok
mov dx,cs
mov ds,dx
mov dx,OFFSET RemovedMsg
jmp MessageAndStop
RemoveFromMemory ENDP
Initialisation PROC NEAR ;Neuen Video-Treiber installieren.
call TestIfResident
je AlreadyResident ;Schon resident?
mov al,VideoCard ;Noch nicht! Dann sollten wir eigentlich
cmp al,Card_Herkules ;resident werden, aber zuerst testen:
mov dx,OFFSET InvCardMsg ;Ist das eine Herkules-Karte?
je MakeResident ;Dann ist es ja prima.
jmp FatalError ;NICHT? Um Gotteswillen, Fehler!
AlreadyResident:
mov al,FastScrollFlag
cmp al,Dunnow ;Wurde eine der FastScroll-Options angegeben?
je RemoveFromMemory ;Nicht? Dann Treiber aus dem Speicher werfen.
cmp es:FastScrollFlag,TRUE ;Ist FastScrolling momentan an?
mov es:FastScrollFlag,al
jne UpdateDone
cmp al,False ;Und soll es jetzt ausgeschaltet werden ("/S")?
jne UpdateDone
mov al,VideoMode
cmp al,8 ;Und sind wir außerdem noch im Graphik-Modus?
jne UpdateDone
mov ah,00h ;SET VIDEO MODE ;Dann muß vorher der DispStart
int 10h ;wieder auf 0 gesetzt werden.
UpdateDone:
mov si,OFFSET PrnInfo
mov di,si
mov cx,PrnInfoSize ;(Eventuell neue) Drucker-Daten
cld ;von hier in das residente
rep movsb ;Programm übertragen.
mov ah,Ret_Ok
mov dx,OFFSET UpdatedMsg
jmp MessageAndStop
Initialisation ENDP
MakeResident PROC NEAR
cmp FastScrollFlag,Dunnow ;Keine Angaben zu FastScroll?
jne FlagIsSet
mov FastScrollFlag,FALSE ;Dann Default=FALSE verwenden.
FlagIsSet:
mov dx,OFFSET EndOfProgram
cmp ds:[6],dx ;Genug Platz für RotateBuffer?
mov dx,OFFSET OutOfMemMsg
jb FatalError ;Nicht? Dann Abbruch.
mov ah,25h ;FunctionCall SET VECTOR
mov al,5h
mov dx,OFFSET NewInt5Handler ;INT 5 auf dieses
int 21h ;Programm umlenken,
mov ah,25h ;FunctionCall SET VECTOR
mov al,10h ;genauso INT 10.
mov dx,OFFSET NewInt10Handler
int 21h
mov dx,ConfigReg ;Da dieser INT10-Handler auch die zweite
mov al,HGC_Text ;VideoPage unterstützt, wird diese
out dx,al ;jetzt in der Herkules-Karte enabled.
push ds
mov ax,BiosData
mov ds,ax ;Außerdem wird die Nummer der letzten Zeile im
ASSUME DS:BiosData ;BiosData-Segment auf 24 gesetzt, das alte Bios
mov LastTextLine,24 ;unterstützt diese Variable nämlich noch nicht.
mov bl,7
mov bh,1
mov cx,0 ;Jetzt sollten wir noch die zweite,
mov dx,25*256+80 ;bis jetzt ja weder benutzte noch initialisierte
call ClearWindow ;Page löschen.
pop ds
ASSUME DS:TheProgram
mov ah,35h ;FunctionCall GET VECTOR
mov al,1Fh ;Vektor $1F zeigt im auf einen
int 21h ;ZeichenSatz mit den oberen 128
cmp bx,0 ;IBM-ASCII-Zeichen für die CGA-
jne GrafTablAlreadyLoaded ;Graph-Modi. Ist dieser Zeichen-
mov ax,es ;Satz schon geladen, ist der
cmp ax,0 ;Zeiger ungleich NIL?
jne GrafTablAlreadyLoaded
mov ah,25h ;FunctionCall SET VECTOR ;Dann Zeiger auf eigenen ZeichenSatz
mov al,1Fh ;setzen, das bringt zwar nur mit
mov dx,OFFSET ZeichenSatz8x8+128*8 ;einem CGA-Simulator etwas, kostet
int 21h ;aber keinen Speicherplatz und deshalb...
GrafTablAlreadyLoaded:
mov ah,9 ;FunctionCall PRINT STRING
mov dx,OFFSET HerkCardMsg
int 21h ;Dann entsprechende
mov dx,OFFSET SuccessMsg ;Message ausgeben,
int 21h
mov ah,31h ;FunctionCall KEEP PROCESS
mov al,Ret_Ok
mov dx,OFFSET EndOfProgram+15
mov cl,4
shr dx,cl
cld
int 21h ;und resident werden.
MakeResident ENDP
FatalError PROC NEAR ;[DS:DX]=ErrorMessage.
push dx
mov dx,OFFSET FatalErrMsg
mov ah,9 ;FunctionCall PRINT STRING
int 21h
pop dx
mov ah,Ret_Error
jmp MessageAndStop
FatalError ENDP
ProcessCommandLine PROC NEAR ;[DS:SI]=CommandLine
cld
lodsb ;Längen-Byte lesen
mov cl,al
mov ch,0 ;und nach CX holen.
ParameterLoop:
jcxz EoCommandLine ;CX=0 => Ende der CmdLine erreicht.
lodsb ;Zeichen holen,
dec cx
cmp al,' '
je ParameterLoop
cmp al,'?' ;ein Fragezeichen?
jne OtherParam
mov ExplainFlag,True ;Dann Flag setzen
jmp SHORT ParameterLoop ;und nächsten Parameter bearbeiten.
OtherParam:
cmp al,OptChar ;Ist es ein "/"?
mov dx,OFFSET InvParamMsg
jne FatalError ;Auch nicht? => Fehler.
mov dx,OFFSET InvOptionMsg ;CommandLine hinter "/" zuende?
jcxz FatalError ;=> Invalid Option.
lodsb
dec cx ;Zeichen hinter "/" holen
cmp al,'a'
jb IsUpcase
cmp al,'z' ;und nach Großschrift wandeln.
ja IsUpcase
sub al,'a'-'A'
IsUpcase:
cmp al,'F' ;Ist es ein "F"?
mov ah,True ;Dann Flag setzen für "Fast Scroll".
je SetScrollFlag
cmp al,'S'
mov ah,False ;Ist es ein "S"? Dann das Flag löschen.
je SetScrollFlag
jmp FatalError ;Weder "F" noch "S"? => Invalid Option.
SetScrollFlag:
mov FastScrollFlag,ah
jmp SHORT ParameterLoop
EoCommandLine:
ret
ProcessCommandLine ENDP
CheckHardware PROC NEAR
mov ah,0Fh ;Get Video Mode
int 10h ;Was für ein Video-Mode ist das denn?
push ax
mov ah,Card_Unknown
cmp al,7 ;VideoMode 7 oder 8? Dann könnte das durchaus
je PossiblyHerkulesCard ;eine Herkules Karte sein, muß aber
cmp al,8 ;noch genauer getestet werden.
jne CheckingDone ;Weder 7 noch 8? => Unbekannte Karte.
PossiblyHerkulesCard: ;Es KÖNNTE also eine sein, muß näher testen:
mov dx,BiosData
mov es,dx ;Port-Adresse der Video-Karte mit der
ASSUME ES:BiosData
cmp es:VidCtrlIoAdr,IndexReg ;der Herkules und MDA vergleichen:
ASSUME ES:NOTHING
jne CheckingDone ;Ungleich? Dann unbekannte Karte.
mov ah,0 ;Read Ticker-Count
int 1Ah ;Uhrzeit in 1/18-Sekunden nach CX:DX holen,
add dx,9 ;9/18 = ½ Sekunde dazu addieren.
adc cx,0
mov WhenTimeOut,dx
mov WhenTimeOut+2,cx
WaitLoop:
mov cx,10000 ;10000 mal nacheinander
mov dx,StatusReg ;das Status-Register
in al,dx ;auslesen:
and al,80h
mov ah,al
CloseLoop: ;Wenn sich das MSB
in al,dx ;(Horizontal Retrace)
and al,80h ;ändert,
cmp al,ah
loope CloseLoop
mov ah,Card_Herkules
jne CheckingDone ;dann ist das eine Herkules-Karte.
mov ah,0 ;Read Ticker-Count
int 1Ah ;Zeit abfragen:
cmp cx,WhenTimeOut+2 ;Ist die ½ Sekunde vorbei?
jb WaitLoop ;Noch nicht? Dann weiter warten.
mov ah,Card_Mono
ja CheckingDone ;Schon vorbei? Dann ist das wohl keine Herkules.
cmp dx,WhenTimeOut
jb WaitLoop
CheckingDone:
mov al,ah
pop dx
mov ah,dl
ret
CheckHardware ENDP ;AL=Karten-Kennung, AH=VideoMode.
TestIfResident PROC NEAR ;Sind wir schon resident?
mov ah,35h ;FunctionCall GET VECTOR
mov al,5h
int 21h
mov SavedInt5Vec,bx ;Vektor für INT 5 (PrtScr)
mov SavedInt5Vec+2,es ;nach ES:BX holen und merken.
mov ah,35h ;FunctionCall GET VECTOR
mov al,10h ;Vektor für INT 10h
int 21h ;nach ES:BX holen
mov SavedInt10Vec,bx ;und merken.
mov SavedInt10Vec+2,es ;Stimmt der Ofs des INT10-Vektors
cmp bx,OFFSET NewInt10Handler ;mit dem des INT10-Handlers überein?
jne NotResident ;Nicht? Dann sind wir noch nicht resident.
cmp SavedInt5Vec,OFFSET NewInt5Handler ;Genauso die Ofs beim
jne NotResident ;INT5 vergleichen...
mov ax,SavedInt10Vec+2 ;Und die Segs vom INT 5-
cmp ax,SavedInt5Vec+2 ;und INT 10-Handler?
jne NotResident
mov si,OFFSET NewInt10Handler+1 ;Um ganz sicher zu gehen, vergleichen
mov di,si ;wir auch noch die Version-Strings.
mov cx,17
cld
rep cmpsb ;Stimmen beide überein?
NotResident:
ret
TestIfResident ENDP ;ZF=1 wenn schon resident. ES = SEG(residenterHandler).
UnknownMsg DB '*** Unknown Graphics Card ***',13,10,'$'
MonoCardMsg DB '*** IBM Monochrome Card detected ***',13,10,'$'
HerkCardMsg DB '*** HGC detected, HERKULES $'
SuccessMsg DB 'made resident ***',13,10,'$'
NotResiMsg DB 'not yet resident ***',13,10,'$'
ResidentMsg DB 'already loaded with FastScrolling $'
OnMsg DB 'ON ***',13,10,'$'
OffMsg DB 'OFF ***',13,10,'$'
UpdatedMsg DB '*** HERKULES already resident, Options updated ***',13,10,'$'
RemovedMsg DB '*** HERKULES removed from Memory ***',13,10,'$'
FatalErrMsg DB '*** Fatal Error: $'
InvCardMsg DB 'This is no Herkules Card ***',13,10,'$'
InvParamMsg DB 'Invalid Parameter ***',13,10,'$'
InvOptionMsg DB 'Invalid Option ***',13,10,'$'
OutOfMemMsg DB 'Out of Memory ***',13,10,'$'
ExplainMsg DB 'This Program is an extension to the INT 10h',13,10
DB 'for support of the Herkules Graphic Card in',13,10
DB 'Hires-Mode (720x360). It does NOT work with',13,10
DB 'the IBM Monochrome Card! By adding the Opt-',13,10
DB 'ions "/F" or "/S", you may specify whether',13,10
DB 'Fast- or Slow-Scrolling is to be used. For',13,10
DB 'FastScroll the DisplayStart-Register of the',13,10
DB '6845 will be changed, which might interfere',13,10
DB 'to some Programs. Checking Hardware:',13,10,'$'
;-----------------------------------------------------------------------------
TheProgram ENDS
END EntryPoint